#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <sys/uio.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include "queue.h"

/* the TCP port that is used for this example */
#define TCP_PORT   6500
#define RESPONSE_PORT   7300
#define MAX 8192

struct thread_data{
	int socket;
	char * ipClient;
};

struct sensore{
	int id;
	char mac[17];
	int rfcomm;
	int canale;
	char *value; //di tipo char perchè il sensore potrebbe restituire più di un intero!
	char attivo; //'s'=connessione a dispositivo bluetooth avvenuta con successo. 'n'=il contrario!
};

//coda di richieste provenienti da wifi & tutti i relativi semafori e mutex
coda_t queue;
sem_t full, empty;
pthread_mutex_t mutex;

//coda di risposte da mandare ai nodi wifi e tutti i relativi semafori e mutex
coda_t queue_resp;
sem_t full_resp, empty_resp;
pthread_mutex_t mutex_resp;

//mutex per i vari thread BLUETOOTH:
pthread_mutex_t M; /* def.mutex condiviso tra threads */ 

//vettore di "sensori" letti da file
struct sensore *s;
int numSensori; //contatore di numSensori


//lista funzioni
void *connect_to_sensor_bluetooth(void *arg);
void *request_handling(void * arg);
void *request_listener();
void *master_bluetooth_function();
void *send_responce_to_wifi();
void *thread1_process (void * arg);


main()
{
	//faccio un sudo all'inizio, così poi vale per sempre! :D
	int error_2 = system("sudo -v");
	if(error_2 == -1){
		printf("\nErrore - PASSWORD SBAGLIATA!\n");
		return(-1);
	}

	//Creazione coda per accodare le richieste proveniente di wifi
	int check = queue_initialization(&queue);
	if (check){
		printf ("\nERRORE NELL'INIZIALIZZAZIONE DELLA CODA di richieste!!!\n");
		exit (1);
	}
	printf("Coda per request creata!\n");
	//set mutex e semafori per coda di richieste
	pthread_mutex_init(&mutex,NULL);
	sem_init(&full,0,0);
	sem_init(&empty,0,DIM_CODA);
	printf("Mutex e Semafori request creati!\n");
	
	
	//Creazione coda per accodare le risposte da mandare ai nodi wifi
	check = queue_initialization(&queue_resp);
	if (check){
		printf ("\nERRORE NELL'INIZIALIZZAZIONE DELLA CODA di risposte!!!\n");
		exit (1);
	}
	printf("Coda per responce creata!\n");
	//set mutex e semafori per coda di risposte
	pthread_mutex_init(&mutex_resp,NULL);
	sem_init(&full_resp,0,0);
	sem_init(&empty_resp,0,DIM_CODA);
	printf("Mutex e Semafori responce creati!\n");
	
	//mutex per contenere i valori dei sensori letti dal Bluetooth
	pthread_mutex_init(&M,NULL);
	
	//lettura degli ID + MAC dei sensori da FILE
	FILE *pf= fopen("config.txt", "r");
	if (pf == NULL){
	      printf("Non posso aprire il file! BYE!");
	      exit(-1);
	}
	
	numSensori=0;
	
	s = (struct sensore *)malloc(sizeof(struct sensore)); //alloco il primo elemento del vettore
	
	int jj=0;
	while(!feof(pf)){
		if(jj!=0){
			s = (struct sensore *)realloc(s,(numSensori+1)*sizeof(struct sensore));		
		}
		
		//riempio la struttura di sensori
		fscanf(pf, "ID:%d\tMAC:%s\tRFCOMM:%d\tCANALE:%d\n",&(s[numSensori].id),(s[numSensori].mac),&(s[numSensori].rfcomm),&(s[numSensori].canale));
		(s[numSensori].value) = "0";
		(s[numSensori].attivo) = 's';
		printf("ID:%d\tMAC:%s\tRFCOMM:%d\tCANALE:%d\tVALUE:%s\n",(s[numSensori].id),(s[numSensori].mac),(s[numSensori].rfcomm),(s[numSensori].canale),s[numSensori].value);
		
		//creo tanti thread quanti sono i numSensori --> scopo: aprire le connessioni bluetooth!
		pthread_t connectorBluetooth;
		pthread_create(&connectorBluetooth,NULL,connect_to_sensor_bluetooth,(void *)&s[numSensori]);
		
		numSensori++;
		jj++;
	}
	fclose(pf);
	
	//metto lo sleep per aspettare che la soekris provi a connettersi ai sensori bluetooth! 
	sleep(10);

	
	//creazione tutti i thread:
	pthread_t listener,master_bluetooth,sender,writerBluetooth;
	pthread_create(&listener,NULL,request_listener,NULL);
	pthread_create(&master_bluetooth,NULL,master_bluetooth_function,NULL);
	pthread_create(&sender,NULL,send_responce_to_wifi,NULL);	
	int indice=0;
	for(indice=0;indice<=numSensori;indice++){
		if(s[indice].attivo=='s'){
			//se sono qui, vuol dire che il sensore è attivo e la soekris si è connessa tramite bluetooth
			printf("\nCreo writerBluetooth");
			pthread_create(&writerBluetooth,NULL,thread1_process,(void *)&s[indice]);
		}
	}
	printf("\nThreads creati!\n");

	pthread_exit(0);

}


/*
Thread che sta ESEGUE il comando di shell.
Ovviamente se ho N sensori... avrò anche N di questi thread... ognuno prova a connettersi ad un diverso dispositivo bluetooth.
Se il dispositivo bluetooth non è attivo viene settato: mioSensore->attivo='n';
*/
void *connect_to_sensor_bluetooth(void *arg){

	printf ("\nCreato connector to bluetooth\n");

	struct sensore *mioSensore = (struct sensore *)arg;

	//char *shell_command = malloc(sizeof("sudo rfcomm -i hci0 connect ")) + malloc(sizeof(rfcomm2));
	char *shell_command = malloc(sizeof("sudo rfcomm -i hci0 connect ") + sizeof(mioSensore->rfcomm) + sizeof(" ") + sizeof(mioSensore->mac) + sizeof(" ") + sizeof(mioSensore->canale));
	sprintf(shell_command,"sudo rfcomm -i hci0 connect %d %s %d",mioSensore->rfcomm,mioSensore->mac,mioSensore->canale);
	
	//printf("\nfine strcat: %s\n",shell_command);

	int err = system(shell_command);
	if(err == -1 || err == 0){
		printf("\nErrore - NON è possibile connettersi al sensore con MAC address: %s\n",mioSensore->mac);
		mioSensore->attivo='n';
	}
		
	pthread_exit(0);
}


/*
Thread che sta in ascolto sulla porta "TCP_PORT" e crea workerThread. 
*/
void *request_listener(){
	int sockfd, newsockfd, clilen;
	struct sockaddr_in cli_addr, serv_addr;
	pthread_t thread_listener_request;
	
	printf("Creazione Listener\n");

	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		printf("server: can't open stream socket\n");

	memset((char *) &serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(TCP_PORT);
	
	if(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
		printf("server: can't bind local address\n");	
	
	listen(sockfd, 200);
	
	printf("Listener Creato e Socket creata\n");

	while(1){
		clilen = sizeof(cli_addr);
		newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);

		if(newsockfd < 0)
			printf("server: accept error\n");
		

		struct sockaddr_in s;
        int s_len = sizeof(s);
        char ip[20];
		inet_ntop(AF_INET,&cli_addr.sin_addr.s_addr,ip,s_len);
		printf("IP DEL CLIENT: %s\n",ip);
		struct thread_data dati_tmp;
		dati_tmp.socket = newsockfd;
		dati_tmp.ipClient = ip;
		
		/* create a new thread to process the incomming request  inet_ntoa(cli_addr.sin_addr)*/
		pthread_create(&thread_listener_request,NULL,request_handling,((void *)&dati_tmp ));
		


		/* the server is now free to accept another socket request */
	}
}//fine request_listener()


/*
WorkerThread.
quando una wifi fa la connect al socket, si crea questo thread che si occupa di eseguire (in ordine) le seguenti operazioni:
- riceve le richieste da parte della wifi (quelle del tipo "idMsg,IdSensore");
- le inserisce in coda;
- controlla lo stato della coda di richieste (perchè potrebbe essere quasi piena).
- manda un messaggio alla wifi per informarla dello stato delal coda ed eventualmente chiederle di rallentare (i messaggi, come detto nel file wifi.c sono di 3 tipi: ACK, 1/2, 3/4).
- riesegue il ciclo di attesa del nuovo messaggio ovviamente però se il messaggio ottenuto è diverso dalla stringa "END//////"
(Questo thread è quindi il produttore di richieste!).
*/
void *request_handling(void * arg){
	
	struct thread_data *datii = (struct thread_data *)arg;
    char * ip = datii->ipClient;
    int socketClient = datii->socket;

	char data[9]="END//////";

	printf("Child thread: Socket number = %d\n",  socketClient);
	printf("Child thread: IP Client = %s\n", ip);
	

	do{
		//read from the given socket
		read(socketClient, data, 9);

		if(strcmp(data,"END//////")!=0){
			
			int numMsg;
			int idSensore;
			sscanf(data,"%d,%d",&numMsg,&idSensore);
			
			printf("\n\nRicevo il numero del pacchetto da inserire in coda : %d",numMsg);
			printf("\nRicevo l'id del sensore : %d\n\n",idSensore);	
	
	
			dati_t tmp;
			tmp.msgNumber = numMsg;
			tmp.sensorID = idSensore;
			tmp.sensorValue = "0";
			tmp.destinationIP = ip;
	
			//verifico che la coda non è piena
			sem_wait(&empty);
			//acquisisco il lock ed entro in sezione critica
			pthread_mutex_lock(&mutex); 
			//inserisco l'elemento in coda
			insert_in_head(&queue,tmp);
			//ottengo il numero di elementi prensenti attualmente in coda
			int num_pacchetti_attuali = queue.num;
			// USCIAMO DALLA SEZIONE CRITICA: Abbiamo finito di operare sulla coda, quindi possiamo liberare il mutex
			pthread_mutex_unlock(&mutex);
			// Incremento il valore del semaforo "full"
			sem_post(&full);
	
			//mando una conferma dell'avvenuta ricezione del pacchetto
			int spazio_libero_in_queue = DIM_CODA - num_pacchetti_attuali;
			printf("num_pacchetti_attuali: %d\n",num_pacchetti_attuali);
			printf("spazio_libero_in_queue: %d\n",spazio_libero_in_queue);
			if(spazio_libero_in_queue >= (DIM_CODA/2)){
				//la coda è poco piena (meno piena del 50%) --> mando un ACK
				send(socketClient,"ACK",sizeof("ACK"),0);
			}else if( spazio_libero_in_queue < (DIM_CODA/2) && num_pacchetti_attuali <= (DIM_CODA*0.75)){
				//la coda è piena tra il 50% e il 75% (cioè meno di 3/4) --> chiedo al wifi di ridurre la sua velocità di metà di quella attuale
				send(socketClient,"1/2",sizeof("1/2"),0);
			}else if( spazio_libero_in_queue < (DIM_CODA*0.75) ){
				//la coda è piena per più del 75% (ma ancora non è satura) --> chiedo al wifi di ridurre la velocità di 3/4 di quella attuale
				send(socketClient,"3/4",sizeof("3/4"),0);
			}

		}
	}while(strcmp(data,"END//////")!=0);
	
	
	printf("\n *** HO ricevuto TUTTI I MESSAGGI *** \n");
	
	//close the socket and exit this thread
	close(socketClient);
	pthread_exit((void *)0);
}//fine request_handling


/*
Thread che si occupa di :
- preleva un elemento dalal coda di richieste (è quindi il consumatore di richieste).
- in base all'idSensore, legge dalal struttura "struct sensore *s" il giusto valore del sensore.
- inserisce una "struct dati_t" dentro la coda di risposte (è quindi produttore di risposte).
*/
void *master_bluetooth_function(){

	printf("Creazione Master Bluetooth\n");

	dati_t dati;
	
	while(1){

		// verifico se c'è almeno un elemento in coda (altrimenti mi blocco)
		sem_wait (&full);
		// ENTRIAMO IN SEZIONE CRITICA: Otteniamo l'accesso esclusivo alla coda
		pthread_mutex_lock(&mutex);
		//estraggo elemento e il suo valore lo ritrovo dentro la variabile "dati"
		remove_from_tail(&queue, &dati);
		// USCIAMO DALLA SEZIONE CRITICA: Abbiamo finito di operare sulla coda... IL valore di mutex ritorna da 0 a 1
		pthread_mutex_unlock(&mutex);
		// Segnaliamo di aver svuotato di un elemento la coda (ovvero, incrementiamo il valore del semaforo "empty")
		sem_post(&empty);
		
//		printf ("Master Bluetooth: Ho estratto ID sensore = %d\n",dati.sensorID);
//		printf ("Master Bluetooth: Ho estratto Value sensore = %s\n",dati.sensorValue);
//		printf ("Master Bluetooth: Ho estratto IP destinazione = %s\n",dati.destinationIP);



		/***************************************  inizio lettura valore bluetooth ******************************/

		

		int value_sensore = -1;
		//int numSensori = sizeof(struct sensore);
		int i=0;
		pthread_mutex_lock(&M); 
		for(i=0;i<numSensori;i++){
			if(s[i].id == dati.sensorID){
				value_sensore = atoi(s[i].value); //conversione da char ad int
			}
		}
		printf("\nLeggo: %d\n",value_sensore);
		pthread_mutex_unlock(&M); 
		
		
		//conversione da int --> a char
		char jj[4];
		sprintf(jj,"%d",value_sensore);
	
		dati.sensorValue = jj;
		


		/***************************************  fine lettura valore bluetooth ******************************/

		
		//verifico che la coda di responce non è piena
		sem_wait(&empty_resp);
		//acquisisco il lock ed entro in sezione critica
		pthread_mutex_lock(&mutex_resp); 
		//inserisco l'elemento in coda
		insert_in_head(&queue_resp,dati);
		// USCIAMO DALLA SEZIONE CRITICA: Abbiamo finito di operare sulla coda, quindi possiamo liberare il mutex
		pthread_mutex_unlock(&mutex_resp);
		// Incremento il valore del semaforo "full"
		sem_post(&full_resp); 
	

	}//fine while
}//fine master_bluetooth_function

/*
Thread che si occupa di "comunicare" con i sensori Bluetooth.
in realtà legge da una specifica seriale /dev/rfcomN.
Per cui per ogni connessione bluetooth ATTIVA verso un certo sensore, avrò uno di questi thread che legge i valori dalla seriale!
I valori che legge li usa per aggiornare la variabile "mioSensore->value" (NB: essendo "mioSensore" una struct condivisa tra tread, ho la necessità di usare mutex!)
PS: Domani vi posto un nuovo modo di comunicare tra soekris e bluetooth... :P
*/
void *thread1_process (void * arg)
{ 
	//ottengo l'intera struttura contenente tutti i dati sul "mioSensore".
	struct sensore *mioSensore = (struct sensore *)arg;
	
	int error=0;
	char buf[4]={0};

	char nomeFile[15];
	sprintf(nomeFile,"/dev/rfcomm%d",mioSensore->rfcomm);
	printf("\nnomeFile: %s",nomeFile);
	/*
	for(yy=0;yy<12;yy++){
		porta[yy]= rfcomm[mioSensore][yy];
	}
	printf("rfcomm:%s\n",porta);
	*/
	int fd= open(nomeFile, O_RDWR|O_NOCTTY|O_NDELAY);
	if (fd == -1){
		perror("open_port: Unable to open /dev/rfcommN - ");
	}
	else{
		printf("\nWriterBluetooth CREATO... l'id del mio sensore è: %d !\n",mioSensore->id);
		while(1){
		fcntl(fd, F_SETFL, 0);
		read(fd, buf, 4);
		//int alue = atoi(buf); //conversione da char ad int
		    //printf("['%d', %d]\n", alue, error);

		  if ( error < 0 ){
			printf("[er]\n");
			return 0;
		  }else{    
			pthread_mutex_lock(&M); /*prologo sez. critica */
			//value[mioSensore] = atoi(buf); //conversione da char ad int
			mioSensore->value=buf;
			printf("io, thread %d sto scrivendo: ['%s', %d]\n", mioSensore->id, mioSensore->value, error);
			pthread_mutex_unlock(&M); /*epilogo sez. critica*/
		  }
		}
	}

	pthread_exit(0);
}


/*
Thread che si occupa di mandare le risposte alla wifi (è quindi il consumatore di risposte).
Se c'è almeno 1 risposta in coda: apre il socket, manda in serie "idMsg"-"idSensore"-"sensorValue" e chiude il socket.
Bye! Bye!
*/
void *send_responce_to_wifi(){
	int sd; /* Il socket descriptor del client */
	struct sockaddr_in server_addr; /* l'indirizzo del server */
	char buff[MAX]; /* dati di invio e ricezione */
	dati_t responce;
 

	while(1){
		// verifico se c'è almeno un elemento in coda (altrimenti mi blocco)
		sem_wait(&full_resp);
		// ENTRIAMO IN SEZIONE CRITICA: Otteniamo l'accesso esclusivo alla coda
		pthread_mutex_lock(&mutex_resp);
		//estraggo elemento e il suo valore lo ritrovo dentro la variabile "idSensoreBluetooth"
		remove_from_tail(&queue_resp, &responce);
		// USCIAMO DALLA SEZIONE CRITICA: Abbiamo finito di operare sulla coda... IL valore di mutex ritorna da 0 a 1
		pthread_mutex_unlock(&mutex_resp);
		// Segnaliamo di aver svuotato di un elemento la coda (ovvero, incrementiamo il valore del semaforo "empty")
		sem_post(&empty_resp);


		/* Utilizzando la struttura hostent si definisce l'indirizzo del server */
		struct hostent *hp;
		hp = gethostbyname(responce.destinationIP);
	 
		server_addr.sin_family = AF_INET;
		server_addr.sin_port = htons(RESPONSE_PORT);
		/* successivamente viene memorizzato nella struttura server_addr */
		server_addr.sin_addr.s_addr = ((struct in_addr*)(hp->h_addr)) -> s_addr;
	 
		/* Viene creato il socket descriptor */
		if((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
			printf("Errore nella creazione della socket\n");
	 
		/* Viene connesso al server */
		if(connect(sd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
			printf("Errore di connessione al server\n");


		char *numMsg = malloc(sizeof(responce.msgNumber));
		sprintf(numMsg, "%d",responce.msgNumber);
		send(sd,numMsg,sizeof(numMsg),0);
		char *idS = malloc(sizeof(responce.sensorID));
		sprintf(idS, "%d",responce.sensorID);
		send(sd,idS,sizeof(idS),0);
		send(sd,responce.sensorValue,sizeof(responce.sensorValue),0);
		printf("\n\n--> Ho inviato: [%s,%s,%s] <--\n",numMsg,idS,responce.sensorValue);
	
		close(sd);
		
		//printf("\n\n inviooo risposta\n\n");
		
	} 
	//pthread_exit(0);
}//fine send_responce_to_wifi
